package me.stone.stanimeclient.page.fan

import android.view.View
import androidx.databinding.ViewDataBinding
import androidx.databinding.ViewStubProxy
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import me.stone.stanimeclient.core.data.domain.Result
import me.stone.stanimeclient.core.data.domain.fan.LoadFanDetailDataCase
import me.stone.stanimeclient.core.model.FanDetailData
import me.stone.stanimeclient.core.model.FanEpisodeItem
import me.stone.stanimeclient.core.ui.R
import me.stone.stanimeclient.core.ui.adapters.FanEpisodeAdapter
import me.stone.stanimeclient.core.ui.databinding.FanEpisodeBinding
import me.stone.stanimeclient.core.ui.databinding.FanOthersBinding
import me.stone.stanimeclient.core.ui.item.EpisodeBindingHelper
import me.stone.stanimeclient.core.ui.item.FanMimeDataBindingHelper
import me.stone.stanimeclient.core.ui.item.FanOtherBindingHelper
import timber.log.Timber
import javax.inject.Inject

@HiltViewModel
class FanInfoViewModel @Inject constructor(
    val mimeDataBindingHelper: FanMimeDataBindingHelper,
    val episodeBindingHelper: EpisodeBindingHelper,
    val seriesBindingHelper: FanOtherBindingHelper,
    val recommendBindingHelper: FanOtherBindingHelper,
    private val loadFanDetailDataCase: LoadFanDetailDataCase
) : ViewModel() {

    private fun <T> Flow<T>.collectIn(block: (T) -> Unit) {
        viewModelScope.launch {
            collectLatest { block.invoke(it) }
        }
    }

    init {
        seriesBindingHelper.updateAdapterMode(false)
        seriesBindingHelper.updateLabelTitle(R.string.label_series_list)
        recommendBindingHelper.updateAdapterMode(true)
        recommendBindingHelper.updateLabelTitle(R.string.label_guess_like_list)
        seriesBindingHelper.otherFanItem.collectIn { loadData(it.id) }
        recommendBindingHelper.otherFanItem.collectIn { loadData(it.id) }
    }

    private fun visibleViewStub(
        viewStub: ViewStubProxy,
        visible: Boolean,
        owner: LifecycleOwner,
        bindDataBlock: (binding: ViewDataBinding) -> Unit
    ) {
        viewStub.binding?.let {
            it.root.visibility = if (visible) View.VISIBLE else View.GONE
        }
        if (viewStub.binding != null || !visible) return
        viewStub.setOnInflateListener { _, _ ->
            viewStub.binding?.let {
                bindDataBlock.invoke(it)
                it.lifecycleOwner = owner
                it.executePendingBindings()
            }
        }
        viewStub.viewStub?.inflate()
    }

    fun visibleEpisode(
        viewStub: ViewStubProxy, visible: Boolean, owner: LifecycleOwner
    ) = visibleViewStub(viewStub, visible, owner) {
        (it as? FanEpisodeBinding)?.let { binding ->
            binding.helper = episodeBindingHelper
            FanEpisodeAdapter.attachRedrawManager(binding.episodes)
        }
    }

    fun visibleSeries(
        viewStub: ViewStubProxy, visible: Boolean, owner: LifecycleOwner
    ) = visibleViewStub(viewStub, visible, owner) {
        (it as? FanOthersBinding)?.let { binding ->
            binding.helper = seriesBindingHelper
        }
    }

    fun visibleRecommend(
        viewStub: ViewStubProxy, visible: Boolean, owner: LifecycleOwner
    ) = visibleViewStub(viewStub, visible, owner) {
        (it as? FanOthersBinding)?.let { binding ->
            binding.helper = recommendBindingHelper
        }
    }

    private val _showPlayer = MutableStateFlow(false)

    val showPlayer: StateFlow<Boolean> = _showPlayer

    val selectedEpisodeItem: StateFlow<FanEpisodeItem?> = episodeBindingHelper
        .selectedEpisodeItemFlow
        .onEach { if (!_showPlayer.value && it != null) _showPlayer.emit(true) }
        .stateIn(viewModelScope, SharingStarted.Eagerly, null)

    fun loadData(id: Int) {
        viewModelScope.launch { _detailId.emit(id) }
    }

    private val _detailId = MutableStateFlow(0)

    val detailId: StateFlow<Int> = _detailId

    @Suppress("UNUSED")
    @OptIn(ExperimentalCoroutinesApi::class)
    val fanDetailData: StateFlow<FanDetailData> = _detailId
        .filter { it > 0 }
        .flatMapLatest { loadFanDetailDataCase(it) }
        .onEach {
            if (it is Result.Error) {
                Timber.d("fan detail load error: ${it.error}")
            }
        }
        .mapNotNull { it.dataOrNull }
        .onEach {
            mimeDataBindingHelper.updateFanMimeData(it.mimeData)
            episodeBindingHelper.update(it.episodeDataList)
            seriesBindingHelper.updateData(it.otherSeriesFanList)
            recommendBindingHelper.updateData(it.recommendList)
        }
        .stateIn(viewModelScope, SharingStarted.Eagerly, FanDetailData.emptyData())

    val canShowEpisodeUI: StateFlow<Boolean> = episodeBindingHelper.hasEpisodeData

    val canShowSeriesUI: StateFlow<Boolean> = seriesBindingHelper.hasFanSimpleList

    val canShowRecommendUI: StateFlow<Boolean> = recommendBindingHelper.hasFanSimpleList

}

